package com.jxmcloud.business.cut.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Redisson 锁工具,简化 try catch 代码
 *
 * @author wang xiang
 */
@Slf4j
public class RedissonLockUtil {

    /**
     * {@link RedissonClient}
     */
    private final RedissonClient redissonClient;

    /**
     * 需要上锁的 key 值
     */
    private final String key;

    /**
     * 尝试获取锁的时间
     */
    private long time = 10;

    /**
     * 锁住的时间
     */
    private long lockTime = 20;

    /**
     * 尝试获取锁的时间单位
     */
    private TimeUnit timeUnit = TimeUnit.SECONDS;

    /**
     * @param redissonClient {@link RedissonClient}
     * @param key            需要上锁的 key 值
     * @author wang xiang
     */
    public RedissonLockUtil(RedissonClient redissonClient, String key) {
        this.redissonClient = redissonClient;
        this.key = key;
    }

    /**
     * @param redissonClient {@link RedissonClient}
     * @param key            需要上锁的 key 值
     * @param time           尝试获取锁的时间
     * @param unit           尝试获取锁的时间单位
     * @author wang xiang
     */
    public RedissonLockUtil(RedissonClient redissonClient, String key, long time, TimeUnit unit) {
        this.redissonClient = redissonClient;
        this.key = key;
        this.time = time;
        this.timeUnit = unit;
    }

    public RedissonLockUtil(RedissonClient redissonClient, String key, long time, long lockTime, TimeUnit unit) {
        this.redissonClient = redissonClient;
        this.key = key;
        this.lockTime = lockTime;
        this.time = time;
        this.timeUnit = unit;
    }

    /**
     * 上锁并执行代码
     *
     * @param operate {@link Process} 需要执行的代码块
     * @author wang xiang
     */
    public void lock(Process operate) {
        RLock lock = redissonClient.getLock(key);
        try {
            TimeInterval timer = DateUtil.timer();
            if (lock.tryLock(time, lockTime,timeUnit)) {
                operate.run();
            } else {
                log.warn("Redisson 获取锁等待超时: 设置等待时间 {} 设置时间单位 {} 实际等待时间 {} 毫秒 ", time, timeUnit, timer.intervalMs());
                throw new RuntimeException("get lock failed: wait time out");
            }
        } catch (InterruptedException exception) {
            throw new RuntimeException("get lock failed: " + exception);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 上锁并执行代码
     *
     * @param supplier {@link Process} 需要执行的代码块
     * @return 上锁并返回代码
     * @author wang
     */
    public <T> T lock(Supplier<T> supplier) {
        RLock lock = redissonClient.getLock(key);
        try {
            TimeInterval timer = DateUtil.timer();
            if (lock.tryLock(time,lockTime, timeUnit)) {
                return supplier.get();
            } else {
                log.warn("Redisson 获取锁等待超时: 设置等待时间 {} 设置时间单位 {} 实际等待时间 {} 毫秒 ", time, timeUnit, timer.intervalMs());
                throw new RuntimeException("get lock failed: wait time out");
            }
        } catch (InterruptedException exception) {
            throw new RuntimeException("get lock failed: " + exception);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

}
